using System;
using System.Collections.Generic;
using System.Linq;
using ExcelTemplateEngine.Configuration;
using ExcelTemplateEngine.Infrastructure.Logging;

namespace ExcelTemplateEngine.Infrastructure.Performance
{
    public class DefaultPerformanceMonitor : IPerformanceMonitor
    {
        private readonly object _lock = new object();
        private readonly Dictionary<string, OperationStats> _operations = new();
        private readonly ITemplateEngineLogger? _logger;
        private readonly PerformanceSettings _settings;
        private DateTime _startTime = DateTime.Now;
        
        public DefaultPerformanceMonitor(ITemplateEngineLogger? logger, PerformanceSettings? settings = null)
        {
            _logger = logger;
            _settings = settings ?? new PerformanceSettings();
        }
        
        public IPerformanceScope BeginOperation(string operationName)
        {
            return new PerformanceScope(this, operationName, _logger, _settings);
        }
        
        public void RecordOperation(string operationName, TimeSpan duration, Dictionary<string, object>? metrics = null)
        {
            lock (_lock)
            {
                if (!_operations.TryGetValue(operationName, out var stats))
                {
                    stats = new OperationStats { OperationName = operationName };
                    _operations[operationName] = stats;
                }
                
                stats.Count++;
                stats.TotalDuration = stats.TotalDuration.Add(duration);
                stats.MinDuration = duration < stats.MinDuration ? duration : stats.MinDuration;
                stats.MaxDuration = duration > stats.MaxDuration ? duration : stats.MaxDuration;
                stats.LastExecuted = DateTime.Now;
            }
            
            if (_settings.LogSlowOperations && duration > _settings.PerformanceWarningThreshold)
            {
                _logger?.LogWarning("Slow operation detected: {Operation} took {Duration}ms", 
                    operationName, duration.TotalMilliseconds);
            }
            
            _logger?.LogPerformance(operationName, duration, metrics);
        }
        
        public PerformanceStatistics GetStatistics()
        {
            lock (_lock)
            {
                return new PerformanceStatistics
                {
                    Operations = new Dictionary<string, OperationStats>(_operations),
                    StartTime = _startTime,
                    LastResetTime = _startTime,
                    TotalOperations = _operations.Values.Sum(s => s.Count)
                };
            }
        }
        
        public void Reset()
        {
            lock (_lock)
            {
                _operations.Clear();
                _startTime = DateTime.Now;
            }
        }
        
        internal void RecordOperationResult(string operationName, bool success)
        {
            lock (_lock)
            {
                if (_operations.TryGetValue(operationName, out var stats))
                {
                    if (success)
                        stats.SuccessCount++;
                    else
                        stats.FailureCount++;
                }
            }
        }
        
        private class PerformanceScope : IPerformanceScope
        {
            private readonly DefaultPerformanceMonitor _monitor;
            private readonly string _operationName;
            private readonly DateTime _startTime;
            private readonly Dictionary<string, object> _metrics = new();
            private readonly ITemplateEngineLogger? _logger;
            private readonly PerformanceSettings _settings;
            private bool _disposed = false;
            private bool _success = true;
            private string? _resultMessage;
            
            public PerformanceScope(DefaultPerformanceMonitor monitor, string operationName, 
                ITemplateEngineLogger? logger, PerformanceSettings settings)
            {
                _monitor = monitor;
                _operationName = operationName;
                _startTime = DateTime.Now;
                _logger = logger;
                _settings = settings;
                
                if (_settings.EnablePerformanceMonitoring)
                {
                    _logger?.LogDebug("Starting operation: {Operation}", operationName);
                }
            }
            
            public void AddMetric(string key, object value)
            {
                _metrics[key] = value;
            }
            
            public void SetResult(bool success, string? message = null)
            {
                _success = success;
                _resultMessage = message;
            }
            
            public void Dispose()
            {
                if (_disposed) return;
                _disposed = true;
                
                var duration = DateTime.Now - _startTime;
                _monitor.RecordOperation(_operationName, duration, _metrics);
                _monitor.RecordOperationResult(_operationName, _success);
                
                if (_settings.EnablePerformanceMonitoring)
                {
                    var statusMsg = _success ? "completed" : "failed";
                    if (!string.IsNullOrEmpty(_resultMessage))
                        statusMsg += $" ({_resultMessage})";
                        
                    _logger?.LogDebug("Operation {Operation} {Status} in {Duration}ms", 
                        _operationName, statusMsg, duration.TotalMilliseconds);
                }
            }
        }
    }
}